#include <Python.h>
#include <udis86.h>

PyObject *module;

char* instruction_types[] = {
    "INSTRUCTION_TYPE_3dnow",
    "INSTRUCTION_TYPE_aaa",
    "INSTRUCTION_TYPE_aad",
    "INSTRUCTION_TYPE_aam",
    "INSTRUCTION_TYPE_aas",
    "INSTRUCTION_TYPE_adc",
    "INSTRUCTION_TYPE_add",
    "INSTRUCTION_TYPE_addpd",
    "INSTRUCTION_TYPE_addps",
    "INSTRUCTION_TYPE_addsd",
    "INSTRUCTION_TYPE_addss",
    "INSTRUCTION_TYPE_addsubpd",
    "INSTRUCTION_TYPE_addsubps",
    "INSTRUCTION_TYPE_and",
    "INSTRUCTION_TYPE_andpd",
    "INSTRUCTION_TYPE_andps",
    "INSTRUCTION_TYPE_andnpd",
    "INSTRUCTION_TYPE_andnps",
    "INSTRUCTION_TYPE_arpl",
    "INSTRUCTION_TYPE_movsxd",
    "INSTRUCTION_TYPE_bound",
    "INSTRUCTION_TYPE_bsf",
    "INSTRUCTION_TYPE_bsr",
    "INSTRUCTION_TYPE_bswap",
    "INSTRUCTION_TYPE_bt",
    "INSTRUCTION_TYPE_btc",
    "INSTRUCTION_TYPE_btr",
    "INSTRUCTION_TYPE_bts",
    "INSTRUCTION_TYPE_call",
    "INSTRUCTION_TYPE_cbw",
    "INSTRUCTION_TYPE_cwde",
    "INSTRUCTION_TYPE_cdqe",
    "INSTRUCTION_TYPE_clc",
    "INSTRUCTION_TYPE_cld",
    "INSTRUCTION_TYPE_clflush",
    "INSTRUCTION_TYPE_clgi",
    "INSTRUCTION_TYPE_cli",
    "INSTRUCTION_TYPE_clts",
    "INSTRUCTION_TYPE_cmc",
    "INSTRUCTION_TYPE_cmovo",
    "INSTRUCTION_TYPE_cmovno",
    "INSTRUCTION_TYPE_cmovb",
    "INSTRUCTION_TYPE_cmovae",
    "INSTRUCTION_TYPE_cmovz",
    "INSTRUCTION_TYPE_cmovnz",
    "INSTRUCTION_TYPE_cmovbe",
    "INSTRUCTION_TYPE_cmova",
    "INSTRUCTION_TYPE_cmovs",
    "INSTRUCTION_TYPE_cmovns",
    "INSTRUCTION_TYPE_cmovp",
    "INSTRUCTION_TYPE_cmovnp",
    "INSTRUCTION_TYPE_cmovl",
    "INSTRUCTION_TYPE_cmovge",
    "INSTRUCTION_TYPE_cmovle",
    "INSTRUCTION_TYPE_cmovg",
    "INSTRUCTION_TYPE_cmp",
    "INSTRUCTION_TYPE_cmppd",
    "INSTRUCTION_TYPE_cmpps",
    "INSTRUCTION_TYPE_cmpsb",
    "INSTRUCTION_TYPE_cmpsw",
    "INSTRUCTION_TYPE_cmpsd",
    "INSTRUCTION_TYPE_cmpsq",
    "INSTRUCTION_TYPE_cmpss",
    "INSTRUCTION_TYPE_cmpxchg",
    "INSTRUCTION_TYPE_cmpxchg8b",
    "INSTRUCTION_TYPE_comisd",
    "INSTRUCTION_TYPE_comiss",
    "INSTRUCTION_TYPE_cpuid",
    "INSTRUCTION_TYPE_cvtdq2pd",
    "INSTRUCTION_TYPE_cvtdq2ps",
    "INSTRUCTION_TYPE_cvtpd2dq",
    "INSTRUCTION_TYPE_cvtpd2pi",
    "INSTRUCTION_TYPE_cvtpd2ps",
    "INSTRUCTION_TYPE_cvtpi2ps",
    "INSTRUCTION_TYPE_cvtpi2pd",
    "INSTRUCTION_TYPE_cvtps2dq",
    "INSTRUCTION_TYPE_cvtps2pi",
    "INSTRUCTION_TYPE_cvtps2pd",
    "INSTRUCTION_TYPE_cvtsd2si",
    "INSTRUCTION_TYPE_cvtsd2ss",
    "INSTRUCTION_TYPE_cvtsi2ss",
    "INSTRUCTION_TYPE_cvtss2si",
    "INSTRUCTION_TYPE_cvtss2sd",
    "INSTRUCTION_TYPE_cvttpd2pi",
    "INSTRUCTION_TYPE_cvttpd2dq",
    "INSTRUCTION_TYPE_cvttps2dq",
    "INSTRUCTION_TYPE_cvttps2pi",
    "INSTRUCTION_TYPE_cvttsd2si",
    "INSTRUCTION_TYPE_cvtsi2sd",
    "INSTRUCTION_TYPE_cvttss2si",
    "INSTRUCTION_TYPE_cwd",
    "INSTRUCTION_TYPE_cdq",
    "INSTRUCTION_TYPE_cqo",
    "INSTRUCTION_TYPE_daa",
    "INSTRUCTION_TYPE_das",
    "INSTRUCTION_TYPE_dec",
    "INSTRUCTION_TYPE_div",
    "INSTRUCTION_TYPE_divpd",
    "INSTRUCTION_TYPE_divps",
    "INSTRUCTION_TYPE_divsd",
    "INSTRUCTION_TYPE_divss",
    "INSTRUCTION_TYPE_emms",
    "INSTRUCTION_TYPE_enter",
    "INSTRUCTION_TYPE_f2xm1",
    "INSTRUCTION_TYPE_fabs",
    "INSTRUCTION_TYPE_fadd",
    "INSTRUCTION_TYPE_faddp",
    "INSTRUCTION_TYPE_fbld",
    "INSTRUCTION_TYPE_fbstp",
    "INSTRUCTION_TYPE_fchs",
    "INSTRUCTION_TYPE_fclex",
    "INSTRUCTION_TYPE_fcmovb",
    "INSTRUCTION_TYPE_fcmove",
    "INSTRUCTION_TYPE_fcmovbe",
    "INSTRUCTION_TYPE_fcmovu",
    "INSTRUCTION_TYPE_fcmovnb",
    "INSTRUCTION_TYPE_fcmovne",
    "INSTRUCTION_TYPE_fcmovnbe",
    "INSTRUCTION_TYPE_fcmovnu",
    "INSTRUCTION_TYPE_fucomi",
    "INSTRUCTION_TYPE_fcom",
    "INSTRUCTION_TYPE_fcom2",
    "INSTRUCTION_TYPE_fcomp3",
    "INSTRUCTION_TYPE_fcomi",
    "INSTRUCTION_TYPE_fucomip",
    "INSTRUCTION_TYPE_fcomip",
    "INSTRUCTION_TYPE_fcomp",
    "INSTRUCTION_TYPE_fcomp5",
    "INSTRUCTION_TYPE_fcompp",
    "INSTRUCTION_TYPE_fcos",
    "INSTRUCTION_TYPE_fdecstp",
    "INSTRUCTION_TYPE_fdiv",
    "INSTRUCTION_TYPE_fdivp",
    "INSTRUCTION_TYPE_fdivr",
    "INSTRUCTION_TYPE_fdivrp",
    "INSTRUCTION_TYPE_femms",
    "INSTRUCTION_TYPE_ffree",
    "INSTRUCTION_TYPE_ffreep",
    "INSTRUCTION_TYPE_ficom",
    "INSTRUCTION_TYPE_ficomp",
    "INSTRUCTION_TYPE_fild",
    "INSTRUCTION_TYPE_fncstp",
    "INSTRUCTION_TYPE_fninit",
    "INSTRUCTION_TYPE_fiadd",
    "INSTRUCTION_TYPE_fidivr",
    "INSTRUCTION_TYPE_fidiv",
    "INSTRUCTION_TYPE_fisub",
    "INSTRUCTION_TYPE_fisubr",
    "INSTRUCTION_TYPE_fist",
    "INSTRUCTION_TYPE_fistp",
    "INSTRUCTION_TYPE_fisttp",
    "INSTRUCTION_TYPE_fld",
    "INSTRUCTION_TYPE_fld1",
    "INSTRUCTION_TYPE_fldl2t",
    "INSTRUCTION_TYPE_fldl2e",
    "INSTRUCTION_TYPE_fldlpi",
    "INSTRUCTION_TYPE_fldlg2",
    "INSTRUCTION_TYPE_fldln2",
    "INSTRUCTION_TYPE_fldz",
    "INSTRUCTION_TYPE_fldcw",
    "INSTRUCTION_TYPE_fldenv",
    "INSTRUCTION_TYPE_fmul",
    "INSTRUCTION_TYPE_fmulp",
    "INSTRUCTION_TYPE_fimul",
    "INSTRUCTION_TYPE_fnop",
    "INSTRUCTION_TYPE_fpatan",
    "INSTRUCTION_TYPE_fprem",
    "INSTRUCTION_TYPE_fprem1",
    "INSTRUCTION_TYPE_fptan",
    "INSTRUCTION_TYPE_frndint",
    "INSTRUCTION_TYPE_frstor",
    "INSTRUCTION_TYPE_fnsave",
    "INSTRUCTION_TYPE_fscale",
    "INSTRUCTION_TYPE_fsin",
    "INSTRUCTION_TYPE_fsincos",
    "INSTRUCTION_TYPE_fsqrt",
    "INSTRUCTION_TYPE_fstp",
    "INSTRUCTION_TYPE_fstp1",
    "INSTRUCTION_TYPE_fstp8",
    "INSTRUCTION_TYPE_fstp9",
    "INSTRUCTION_TYPE_fst",
    "INSTRUCTION_TYPE_fnstcw",
    "INSTRUCTION_TYPE_fnstenv",
    "INSTRUCTION_TYPE_fnstsw",
    "INSTRUCTION_TYPE_fsub",
    "INSTRUCTION_TYPE_fsubp",
    "INSTRUCTION_TYPE_fsubr",
    "INSTRUCTION_TYPE_fsubrp",
    "INSTRUCTION_TYPE_ftst",
    "INSTRUCTION_TYPE_fucom",
    "INSTRUCTION_TYPE_fucomp",
    "INSTRUCTION_TYPE_fucompp",
    "INSTRUCTION_TYPE_fxam",
    "INSTRUCTION_TYPE_fxch",
    "INSTRUCTION_TYPE_fxch4",
    "INSTRUCTION_TYPE_fxch7",
    "INSTRUCTION_TYPE_fxrstor",
    "INSTRUCTION_TYPE_fxsave",
    "INSTRUCTION_TYPE_fpxtract",
    "INSTRUCTION_TYPE_fyl2x",
    "INSTRUCTION_TYPE_fyl2xp1",
    "INSTRUCTION_TYPE_haddpd",
    "INSTRUCTION_TYPE_haddps",
    "INSTRUCTION_TYPE_hlt",
    "INSTRUCTION_TYPE_hsubpd",
    "INSTRUCTION_TYPE_hsubps",
    "INSTRUCTION_TYPE_idiv",
    "INSTRUCTION_TYPE_in",
    "INSTRUCTION_TYPE_imul",
    "INSTRUCTION_TYPE_inc",
    "INSTRUCTION_TYPE_insb",
    "INSTRUCTION_TYPE_insw",
    "INSTRUCTION_TYPE_insd",
    "INSTRUCTION_TYPE_int1",
    "INSTRUCTION_TYPE_int3",
    "INSTRUCTION_TYPE_int",
    "INSTRUCTION_TYPE_into",
    "INSTRUCTION_TYPE_invd",
    "INSTRUCTION_TYPE_invlpg",
    "INSTRUCTION_TYPE_invlpga",
    "INSTRUCTION_TYPE_iretw",
    "INSTRUCTION_TYPE_iretd",
    "INSTRUCTION_TYPE_iretq",
    "INSTRUCTION_TYPE_jo",
    "INSTRUCTION_TYPE_jno",
    "INSTRUCTION_TYPE_jb",
    "INSTRUCTION_TYPE_jae",
    "INSTRUCTION_TYPE_jz",
    "INSTRUCTION_TYPE_jnz",
    "INSTRUCTION_TYPE_jbe",
    "INSTRUCTION_TYPE_ja",
    "INSTRUCTION_TYPE_js",
    "INSTRUCTION_TYPE_jns",
    "INSTRUCTION_TYPE_jp",
    "INSTRUCTION_TYPE_jnp",
    "INSTRUCTION_TYPE_jl",
    "INSTRUCTION_TYPE_jge",
    "INSTRUCTION_TYPE_jle",
    "INSTRUCTION_TYPE_jg",
    "INSTRUCTION_TYPE_jcxz",
    "INSTRUCTION_TYPE_jecxz",
    "INSTRUCTION_TYPE_jrcxz",
    "INSTRUCTION_TYPE_jmp",
    "INSTRUCTION_TYPE_lahf",
    "INSTRUCTION_TYPE_lar",
    "INSTRUCTION_TYPE_lddqu",
    "INSTRUCTION_TYPE_ldmxcsr",
    "INSTRUCTION_TYPE_lds",
    "INSTRUCTION_TYPE_lea",
    "INSTRUCTION_TYPE_les",
    "INSTRUCTION_TYPE_lfs",
    "INSTRUCTION_TYPE_lgs",
    "INSTRUCTION_TYPE_lidt",
    "INSTRUCTION_TYPE_lss",
    "INSTRUCTION_TYPE_leave",
    "INSTRUCTION_TYPE_lfence",
    "INSTRUCTION_TYPE_lgdt",
    "INSTRUCTION_TYPE_lldt",
    "INSTRUCTION_TYPE_lmsw",
    "INSTRUCTION_TYPE_lock",
    "INSTRUCTION_TYPE_lodsb",
    "INSTRUCTION_TYPE_lodsw",
    "INSTRUCTION_TYPE_lodsd",
    "INSTRUCTION_TYPE_lodsq",
    "INSTRUCTION_TYPE_loopnz",
    "INSTRUCTION_TYPE_loope",
    "INSTRUCTION_TYPE_loop",
    "INSTRUCTION_TYPE_lsl",
    "INSTRUCTION_TYPE_ltr",
    "INSTRUCTION_TYPE_maskmovq",
    "INSTRUCTION_TYPE_maxpd",
    "INSTRUCTION_TYPE_maxps",
    "INSTRUCTION_TYPE_maxsd",
    "INSTRUCTION_TYPE_maxss",
    "INSTRUCTION_TYPE_mfence",
    "INSTRUCTION_TYPE_minpd",
    "INSTRUCTION_TYPE_minps",
    "INSTRUCTION_TYPE_minsd",
    "INSTRUCTION_TYPE_minss",
    "INSTRUCTION_TYPE_monitor",
    "INSTRUCTION_TYPE_mov",
    "INSTRUCTION_TYPE_movapd",
    "INSTRUCTION_TYPE_movaps",
    "INSTRUCTION_TYPE_movd",
    "INSTRUCTION_TYPE_movddup",
    "INSTRUCTION_TYPE_movdqa",
    "INSTRUCTION_TYPE_movdqu",
    "INSTRUCTION_TYPE_movdq2q",
    "INSTRUCTION_TYPE_movhpd",
    "INSTRUCTION_TYPE_movhps",
    "INSTRUCTION_TYPE_movlhps",
    "INSTRUCTION_TYPE_movlpd",
    "INSTRUCTION_TYPE_movlps",
    "INSTRUCTION_TYPE_movhlps",
    "INSTRUCTION_TYPE_movmskpd",
    "INSTRUCTION_TYPE_movmskps",
    "INSTRUCTION_TYPE_movntdq",
    "INSTRUCTION_TYPE_movnti",
    "INSTRUCTION_TYPE_movntpd",
    "INSTRUCTION_TYPE_movntps",
    "INSTRUCTION_TYPE_movntq",
    "INSTRUCTION_TYPE_movq",
    "INSTRUCTION_TYPE_movqa",
    "INSTRUCTION_TYPE_movq2dq",
    "INSTRUCTION_TYPE_movsb",
    "INSTRUCTION_TYPE_movsw",
    "INSTRUCTION_TYPE_movsd",
    "INSTRUCTION_TYPE_movsq",
    "INSTRUCTION_TYPE_movsldup",
    "INSTRUCTION_TYPE_movshdup",
    "INSTRUCTION_TYPE_movss",
    "INSTRUCTION_TYPE_movsx",
    "INSTRUCTION_TYPE_movupd",
    "INSTRUCTION_TYPE_movups",
    "INSTRUCTION_TYPE_movzx",
    "INSTRUCTION_TYPE_mul",
    "INSTRUCTION_TYPE_mulpd",
    "INSTRUCTION_TYPE_mulps",
    "INSTRUCTION_TYPE_mulsd",
    "INSTRUCTION_TYPE_mulss",
    "INSTRUCTION_TYPE_mwait",
    "INSTRUCTION_TYPE_neg",
    "INSTRUCTION_TYPE_nop",
    "INSTRUCTION_TYPE_not",
    "INSTRUCTION_TYPE_or",
    "INSTRUCTION_TYPE_orpd",
    "INSTRUCTION_TYPE_orps",
    "INSTRUCTION_TYPE_out",
    "INSTRUCTION_TYPE_outsb",
    "INSTRUCTION_TYPE_outsw",
    "INSTRUCTION_TYPE_outsd",
    "INSTRUCTION_TYPE_outsq",
    "INSTRUCTION_TYPE_packsswb",
    "INSTRUCTION_TYPE_packssdw",
    "INSTRUCTION_TYPE_packuswb",
    "INSTRUCTION_TYPE_paddb",
    "INSTRUCTION_TYPE_paddw",
    "INSTRUCTION_TYPE_paddq",
    "INSTRUCTION_TYPE_paddsb",
    "INSTRUCTION_TYPE_paddsw",
    "INSTRUCTION_TYPE_paddusb",
    "INSTRUCTION_TYPE_paddusw",
    "INSTRUCTION_TYPE_pand",
    "INSTRUCTION_TYPE_pandn",
    "INSTRUCTION_TYPE_pause",
    "INSTRUCTION_TYPE_pavgb",
    "INSTRUCTION_TYPE_pavgw",
    "INSTRUCTION_TYPE_pcmpeqb",
    "INSTRUCTION_TYPE_pcmpeqw",
    "INSTRUCTION_TYPE_pcmpeqd",
    "INSTRUCTION_TYPE_pcmpgtb",
    "INSTRUCTION_TYPE_pcmpgtw",
    "INSTRUCTION_TYPE_pcmpgtd",
    "INSTRUCTION_TYPE_pextrw",
    "INSTRUCTION_TYPE_pinsrw",
    "INSTRUCTION_TYPE_pmaddwd",
    "INSTRUCTION_TYPE_pmaxsw",
    "INSTRUCTION_TYPE_pmaxub",
    "INSTRUCTION_TYPE_pminsw",
    "INSTRUCTION_TYPE_pminub",
    "INSTRUCTION_TYPE_pmovmskb",
    "INSTRUCTION_TYPE_pmulhuw",
    "INSTRUCTION_TYPE_pmulhw",
    "INSTRUCTION_TYPE_pmullw",
    "INSTRUCTION_TYPE_pmuludq",
    "INSTRUCTION_TYPE_pop",
    "INSTRUCTION_TYPE_popa",
    "INSTRUCTION_TYPE_popad",
    "INSTRUCTION_TYPE_popfw",
    "INSTRUCTION_TYPE_popfd",
    "INSTRUCTION_TYPE_popfq",
    "INSTRUCTION_TYPE_por",
    "INSTRUCTION_TYPE_prefetch",
    "INSTRUCTION_TYPE_prefetchnta",
    "INSTRUCTION_TYPE_prefetcht0",
    "INSTRUCTION_TYPE_prefetcht1",
    "INSTRUCTION_TYPE_prefetcht2",
    "INSTRUCTION_TYPE_psadbw",
    "INSTRUCTION_TYPE_pshufd",
    "INSTRUCTION_TYPE_pshufhw",
    "INSTRUCTION_TYPE_pshuflw",
    "INSTRUCTION_TYPE_pshufw",
    "INSTRUCTION_TYPE_pslldq",
    "INSTRUCTION_TYPE_psllw",
    "INSTRUCTION_TYPE_pslld",
    "INSTRUCTION_TYPE_psllq",
    "INSTRUCTION_TYPE_psraw",
    "INSTRUCTION_TYPE_psrad",
    "INSTRUCTION_TYPE_psrlw",
    "INSTRUCTION_TYPE_psrld",
    "INSTRUCTION_TYPE_psrlq",
    "INSTRUCTION_TYPE_psrldq",
    "INSTRUCTION_TYPE_psubb",
    "INSTRUCTION_TYPE_psubw",
    "INSTRUCTION_TYPE_psubd",
    "INSTRUCTION_TYPE_psubq",
    "INSTRUCTION_TYPE_psubsb",
    "INSTRUCTION_TYPE_psubsw",
    "INSTRUCTION_TYPE_psubusb",
    "INSTRUCTION_TYPE_psubusw",
    "INSTRUCTION_TYPE_punpckhbw",
    "INSTRUCTION_TYPE_punpckhwd",
    "INSTRUCTION_TYPE_punpckhdq",
    "INSTRUCTION_TYPE_punpckhqdq",
    "INSTRUCTION_TYPE_punpcklbw",
    "INSTRUCTION_TYPE_punpcklwd",
    "INSTRUCTION_TYPE_punpckldq",
    "INSTRUCTION_TYPE_punpcklqdq",
    "INSTRUCTION_TYPE_pi2fw",
    "INSTRUCTION_TYPE_pi2fd",
    "INSTRUCTION_TYPE_pf2iw",
    "INSTRUCTION_TYPE_pf2id",
    "INSTRUCTION_TYPE_pfnacc",
    "INSTRUCTION_TYPE_pfpnacc",
    "INSTRUCTION_TYPE_pfcmpge",
    "INSTRUCTION_TYPE_pfmin",
    "INSTRUCTION_TYPE_pfrcp",
    "INSTRUCTION_TYPE_pfrsqrt",
    "INSTRUCTION_TYPE_pfsub",
    "INSTRUCTION_TYPE_pfadd",
    "INSTRUCTION_TYPE_pfcmpgt",
    "INSTRUCTION_TYPE_pfmax",
    "INSTRUCTION_TYPE_pfrcpit1",
    "INSTRUCTION_TYPE_pfrspit1",
    "INSTRUCTION_TYPE_pfsubr",
    "INSTRUCTION_TYPE_pfacc",
    "INSTRUCTION_TYPE_pfcmpeq",
    "INSTRUCTION_TYPE_pfmul",
    "INSTRUCTION_TYPE_pfrcpit2",
    "INSTRUCTION_TYPE_pmulhrw",
    "INSTRUCTION_TYPE_pswapd",
    "INSTRUCTION_TYPE_pavgusb",
    "INSTRUCTION_TYPE_push",
    "INSTRUCTION_TYPE_pusha",
    "INSTRUCTION_TYPE_pushad",
    "INSTRUCTION_TYPE_pushfw",
    "INSTRUCTION_TYPE_pushfd",
    "INSTRUCTION_TYPE_pushfq",
    "INSTRUCTION_TYPE_pxor",
    "INSTRUCTION_TYPE_rcl",
    "INSTRUCTION_TYPE_rcr",
    "INSTRUCTION_TYPE_rol",
    "INSTRUCTION_TYPE_ror",
    "INSTRUCTION_TYPE_rcpps",
    "INSTRUCTION_TYPE_rcpss",
    "INSTRUCTION_TYPE_rdmsr",
    "INSTRUCTION_TYPE_rdpmc",
    "INSTRUCTION_TYPE_rdtsc",
    "INSTRUCTION_TYPE_rdtscp",
    "INSTRUCTION_TYPE_repne",
    "INSTRUCTION_TYPE_rep",
    "INSTRUCTION_TYPE_ret",
    "INSTRUCTION_TYPE_retf",
    "INSTRUCTION_TYPE_rsm",
    "INSTRUCTION_TYPE_rsqrtps",
    "INSTRUCTION_TYPE_rsqrtss",
    "INSTRUCTION_TYPE_sahf",
    "INSTRUCTION_TYPE_sal",
    "INSTRUCTION_TYPE_salc",
    "INSTRUCTION_TYPE_sar",
    "INSTRUCTION_TYPE_shl",
    "INSTRUCTION_TYPE_shr",
    "INSTRUCTION_TYPE_sbb",
    "INSTRUCTION_TYPE_scasb",
    "INSTRUCTION_TYPE_scasw",
    "INSTRUCTION_TYPE_scasd",
    "INSTRUCTION_TYPE_scasq",
    "INSTRUCTION_TYPE_seto",
    "INSTRUCTION_TYPE_setno",
    "INSTRUCTION_TYPE_setb",
    "INSTRUCTION_TYPE_setnb",
    "INSTRUCTION_TYPE_setz",
    "INSTRUCTION_TYPE_setnz",
    "INSTRUCTION_TYPE_setbe",
    "INSTRUCTION_TYPE_seta",
    "INSTRUCTION_TYPE_sets",
    "INSTRUCTION_TYPE_setns",
    "INSTRUCTION_TYPE_setp",
    "INSTRUCTION_TYPE_setnp",
    "INSTRUCTION_TYPE_setl",
    "INSTRUCTION_TYPE_setge",
    "INSTRUCTION_TYPE_setle",
    "INSTRUCTION_TYPE_setg",
    "INSTRUCTION_TYPE_sfence",
    "INSTRUCTION_TYPE_sgdt",
    "INSTRUCTION_TYPE_shld",
    "INSTRUCTION_TYPE_shrd",
    "INSTRUCTION_TYPE_shufpd",
    "INSTRUCTION_TYPE_shufps",
    "INSTRUCTION_TYPE_sidt",
    "INSTRUCTION_TYPE_sldt",
    "INSTRUCTION_TYPE_smsw",
    "INSTRUCTION_TYPE_sqrtps",
    "INSTRUCTION_TYPE_sqrtpd",
    "INSTRUCTION_TYPE_sqrtsd",
    "INSTRUCTION_TYPE_sqrtss",
    "INSTRUCTION_TYPE_stc",
    "INSTRUCTION_TYPE_std",
    "INSTRUCTION_TYPE_stgi",
    "INSTRUCTION_TYPE_sti",
    "INSTRUCTION_TYPE_skinit",
    "INSTRUCTION_TYPE_stmxcsr",
    "INSTRUCTION_TYPE_stosb",
    "INSTRUCTION_TYPE_stosw",
    "INSTRUCTION_TYPE_stosd",
    "INSTRUCTION_TYPE_stosq",
    "INSTRUCTION_TYPE_str",
    "INSTRUCTION_TYPE_sub",
    "INSTRUCTION_TYPE_subpd",
    "INSTRUCTION_TYPE_subps",
    "INSTRUCTION_TYPE_subsd",
    "INSTRUCTION_TYPE_subss",
    "INSTRUCTION_TYPE_swapgs",
    "INSTRUCTION_TYPE_syscall",
    "INSTRUCTION_TYPE_sysenter",
    "INSTRUCTION_TYPE_sysexit",
    "INSTRUCTION_TYPE_sysret",
    "INSTRUCTION_TYPE_test",
    "INSTRUCTION_TYPE_ucomisd",
    "INSTRUCTION_TYPE_ucomiss",
    "INSTRUCTION_TYPE_ud2",
    "INSTRUCTION_TYPE_unpckhpd",
    "INSTRUCTION_TYPE_unpckhps",
    "INSTRUCTION_TYPE_unpcklps",
    "INSTRUCTION_TYPE_unpcklpd",
    "INSTRUCTION_TYPE_verr",
    "INSTRUCTION_TYPE_verw",
    "INSTRUCTION_TYPE_vmcall",
    "INSTRUCTION_TYPE_vmclear",
    "INSTRUCTION_TYPE_vmxon",
    "INSTRUCTION_TYPE_vmptrld",
    "INSTRUCTION_TYPE_vmptrst",
    "INSTRUCTION_TYPE_vmresume",
    "INSTRUCTION_TYPE_vmxoff",
    "INSTRUCTION_TYPE_vmrun",
    "INSTRUCTION_TYPE_vmmcall",
    "INSTRUCTION_TYPE_vmload",
    "INSTRUCTION_TYPE_vmsave",
    "INSTRUCTION_TYPE_wait",
    "INSTRUCTION_TYPE_wbinvd",
    "INSTRUCTION_TYPE_wrmsr",
    "INSTRUCTION_TYPE_xadd",
    "INSTRUCTION_TYPE_xchg",
    "INSTRUCTION_TYPE_xlatb",
    "INSTRUCTION_TYPE_xor",
    "INSTRUCTION_TYPE_xorpd",
    "INSTRUCTION_TYPE_xorps",
    "INSTRUCTION_TYPE_db",
    "INSTRUCTION_TYPE_invalid",
    "INSTRUCTION_TYPE_d3vil",
    "INSTRUCTION_TYPE_na",
    "INSTRUCTION_TYPE_grp_reg",
    "INSTRUCTION_TYPE_grp_rm",
    "INSTRUCTION_TYPE_grp_vendor",
    "INSTRUCTION_TYPE_grp_x87",
    "INSTRUCTION_TYPE_grp_mode",
    "INSTRUCTION_TYPE_grp_osize",
    "INSTRUCTION_TYPE_grp_asize",
    "INSTRUCTION_TYPE_grp_mod",
    "INSTRUCTION_TYPE_none",
    NULL
};

char* operand_types[] = {
    "OPERAND_TYPE_NONE",
    "OPERAND_TYPE_R_AL",
    "OPERAND_TYPE_R_CL",
    "OPERAND_TYPE_R_DL",
    "OPERAND_TYPE_R_BL",
    "OPERAND_TYPE_R_AH",
    "OPERAND_TYPE_R_CH",
    "OPERAND_TYPE_R_DH",
    "OPERAND_TYPE_R_BH",
    "OPERAND_TYPE_R_SPL",
    "OPERAND_TYPE_R_BPL",
    "OPERAND_TYPE_R_SIL",
    "OPERAND_TYPE_R_DIL",
    "OPERAND_TYPE_R_R8B",
    "OPERAND_TYPE_R_R9B",
    "OPERAND_TYPE_R_R10B",
    "OPERAND_TYPE_R_R11B",
    "OPERAND_TYPE_R_R12B",
    "OPERAND_TYPE_R_R13B",
    "OPERAND_TYPE_R_R14B",
    "OPERAND_TYPE_R_R15B",
    "OPERAND_TYPE_R_AX",
    "OPERAND_TYPE_R_CX",
    "OPERAND_TYPE_R_DX",
    "OPERAND_TYPE_R_BX",
    "OPERAND_TYPE_R_SP",
    "OPERAND_TYPE_R_BP",
    "OPERAND_TYPE_R_SI",
    "OPERAND_TYPE_R_DI",
    "OPERAND_TYPE_R_R8W",
    "OPERAND_TYPE_R_R9W",
    "OPERAND_TYPE_R_R10W",
    "OPERAND_TYPE_R_R11W",
    "OPERAND_TYPE_R_R12W",
    "OPERAND_TYPE_R_R13W",
    "OPERAND_TYPE_R_R14W",
    "OPERAND_TYPE_R_R15W",
    "OPERAND_TYPE_R_EAX",
    "OPERAND_TYPE_R_ECX",
    "OPERAND_TYPE_R_EDX",
    "OPERAND_TYPE_R_EBX",
    "OPERAND_TYPE_R_ESP",
    "OPERAND_TYPE_R_EBP",
    "OPERAND_TYPE_R_ESI",
    "OPERAND_TYPE_R_EDI",
    "OPERAND_TYPE_R_R8D",
    "OPERAND_TYPE_R_R9D",
    "OPERAND_TYPE_R_R10D",
    "OPERAND_TYPE_R_R11D",
    "OPERAND_TYPE_R_R12D",
    "OPERAND_TYPE_R_R13D",
    "OPERAND_TYPE_R_R14D",
    "OPERAND_TYPE_R_R15D",
    "OPERAND_TYPE_R_RAX",
    "OPERAND_TYPE_R_RCX",
    "OPERAND_TYPE_R_RDX",
    "OPERAND_TYPE_R_RBX",
    "OPERAND_TYPE_R_RSP",
    "OPERAND_TYPE_R_RBP",
    "OPERAND_TYPE_R_RSI",
    "OPERAND_TYPE_R_RDI",
    "OPERAND_TYPE_R_R8",
    "OPERAND_TYPE_R_R9",
    "OPERAND_TYPE_R_R10",
    "OPERAND_TYPE_R_R11",
    "OPERAND_TYPE_R_R12",
    "OPERAND_TYPE_R_R13",
    "OPERAND_TYPE_R_R14",
    "OPERAND_TYPE_R_R15",
    "OPERAND_TYPE_R_ES",
    "OPERAND_TYPE_R_CS",
    "OPERAND_TYPE_R_SS",
    "OPERAND_TYPE_R_DS",
    "OPERAND_TYPE_R_FS",
    "OPERAND_TYPE_R_GS",
    "OPERAND_TYPE_R_CR0",
    "OPERAND_TYPE_R_CR1",
    "OPERAND_TYPE_R_CR2",
    "OPERAND_TYPE_R_CR3",
    "OPERAND_TYPE_R_CR4",
    "OPERAND_TYPE_R_CR5",
    "OPERAND_TYPE_R_CR6",
    "OPERAND_TYPE_R_CR7",
    "OPERAND_TYPE_R_CR8",
    "OPERAND_TYPE_R_CR9",
    "OPERAND_TYPE_R_CR10",
    "OPERAND_TYPE_R_CR11",
    "OPERAND_TYPE_R_CR12",
    "OPERAND_TYPE_R_CR13",
    "OPERAND_TYPE_R_CR14",
    "OPERAND_TYPE_R_CR15",
    "OPERAND_TYPE_R_DR0",
    "OPERAND_TYPE_R_DR1",
    "OPERAND_TYPE_R_DR2",
    "OPERAND_TYPE_R_DR3",
    "OPERAND_TYPE_R_DR4",
    "OPERAND_TYPE_R_DR5",
    "OPERAND_TYPE_R_DR6",
    "OPERAND_TYPE_R_DR7",
    "OPERAND_TYPE_R_DR8",
    "OPERAND_TYPE_R_DR9",
    "OPERAND_TYPE_R_DR10",
    "OPERAND_TYPE_R_DR11",
    "OPERAND_TYPE_R_DR12",
    "OPERAND_TYPE_R_DR13",
    "OPERAND_TYPE_R_DR14",
    "OPERAND_TYPE_R_DR15",
    "OPERAND_TYPE_R_MM0",
    "OPERAND_TYPE_R_MM1",
    "OPERAND_TYPE_R_MM2",
    "OPERAND_TYPE_R_MM3",
    "OPERAND_TYPE_R_MM4",
    "OPERAND_TYPE_R_MM5",
    "OPERAND_TYPE_R_MM6",
    "OPERAND_TYPE_R_MM7",
    "OPERAND_TYPE_R_ST0",
    "OPERAND_TYPE_R_ST1",
    "OPERAND_TYPE_R_ST2",
    "OPERAND_TYPE_R_ST3",
    "OPERAND_TYPE_R_ST4",
    "OPERAND_TYPE_R_ST5",
    "OPERAND_TYPE_R_ST6",
    "OPERAND_TYPE_R_ST7",
    "OPERAND_TYPE_R_XMM0",
    "OPERAND_TYPE_R_XMM1",
    "OPERAND_TYPE_R_XMM2",
    "OPERAND_TYPE_R_XMM3",
    "OPERAND_TYPE_R_XMM4",
    "OPERAND_TYPE_R_XMM5",
    "OPERAND_TYPE_R_XMM6",
    "OPERAND_TYPE_R_XMM7",
    "OPERAND_TYPE_R_XMM8",
    "OPERAND_TYPE_R_XMM9",
    "OPERAND_TYPE_R_XMM10",
    "OPERAND_TYPE_R_XMM11",
    "OPERAND_TYPE_R_XMM12",
    "OPERAND_TYPE_R_XMM13",
    "OPERAND_TYPE_R_XMM14",
    "OPERAND_TYPE_R_XMM15",
    "OPERAND_TYPE_R_RIP",
    "OPERAND_TYPE_OP_REG",
    "OPERAND_TYPE_OP_MEM",
    "OPERAND_TYPE_OP_PTR",
    "OPERAND_TYPE_OP_IMM",
    "OPERAND_TYPE_OP_JIMM",
    "OPERAND_TYPE_OP_CONST",
    NULL
};

// Helper methods

/*
 Check whether we got a Python Object
 */
PyObject *check_object(PyObject *pObject)
{
	PyObject *pException;
	
	if(!pObject) {
		pException = PyErr_Occurred();
		if(pException)
            PyErr_Print();
        return NULL;
	}
    
    return pObject;
}


/*
 Assign an attribute "attr" named "name" to an object "obj"
 */
void assign_attribute(PyObject *obj, char *name, PyObject *attr)
{
    PyObject_SetAttrString(obj, name, attr);
    Py_DECREF(attr);
}


/*
 Get an attribute named "attr_name" from object "obj"
 The function steals the reference! note the decrement of
 the reference count.
 */
PyObject *get_attribute(PyObject *obj, char *attr_name)
{
    PyObject *pObj;
    
    pObj = PyObject_GetAttrString(obj, attr_name);
	if(!check_object(pObj)) {
        PyErr_SetString(PyExc_ValueError, "Can't get attribute from object");
        return NULL;
    }
    
    Py_DECREF(pObj);
    return pObj;
}


/*
 Get an Long attribute named "attr_name" from object "obj" and
 return it as a "long int"
 */
long int get_long_attribute(PyObject *o, char *attr_name)
{
    PyObject *pObj;
    
    pObj = get_attribute(o, attr_name);
	if(!pObj)
        return 0;
    
    return PyLong_AsLong(pObj);;
}


/*
 Create a new class and take care of decrementing references.
 */
PyObject *create_class(char *class_name)
{
    PyObject *pClass;
    PyObject *pClassDict = PyDict_New();
    PyObject *pClassName = PyString_FromString(class_name);
    
    pClass = PyClass_New(NULL, pClassDict, pClassName);
    if(!check_object(pClass))
        return NULL;
    
    Py_DECREF(pClassDict);
    Py_DECREF(pClassName);
    
    return pClass;
}

/*
 Create an "Operand" Python object from an ud_operand_t structure.
 */
PyObject *create_operand_object(ud_operand_t *op)
{
    PyObject *pOperand = create_class("Operand");
    
    if(!pOperand)
        return NULL;
    
    assign_attribute(pOperand, "type", PyLong_FromLong(op->type));
    assign_attribute(pOperand, "size", PyLong_FromLong(op->size));
    assign_attribute(pOperand, "base", PyLong_FromLong(op->base));
    assign_attribute(pOperand, "index", PyLong_FromLong(op->index));
    assign_attribute(pOperand, "scale", PyLong_FromLong(op->scale));
    assign_attribute(pOperand, "offset", PyLong_FromLong(op->offset));
    switch (op->size) {
        case 8:
            assign_attribute(pOperand, "lval", PyLong_FromLong(op->lval.ubyte));
            break;
        case 16:
            assign_attribute(pOperand, "lval", PyLong_FromLong(op->lval.uword));
            break;
        case 32:
            assign_attribute(pOperand, "lval", PyLong_FromLong(op->lval.udword));
            break;
        default:
            assign_attribute(pOperand, "lval", PyLong_FromUnsignedLongLong(op->lval.uqword));
            break;
    }

    return pOperand;
}


/*
 Create an "Instruction" Python object from an ud_t structure.
 */

PyObject *create_instruction_object(ud_t *my_ud_obj)
{
    PyObject *pInstruction = create_class("Instruction");
    
    if(!pInstruction)
        return NULL;
    
    assign_attribute(pInstruction, "mnemonic", PyLong_FromLong(my_ud_obj->mnemonic));

    assign_attribute(pInstruction, "op1", create_operand_object(&my_ud_obj->operand[0]));
    assign_attribute(pInstruction, "op2", create_operand_object(&my_ud_obj->operand[1]));
    assign_attribute(pInstruction, "op3", create_operand_object(&my_ud_obj->operand[2]));
    
    assign_attribute(pInstruction, "length", PyLong_FromLong(ud_insn_len(my_ud_obj)));
    
    return pInstruction;
}

#define GET_INSTRUCTION_DOCSTRING                                               \
    "Decode an instruction from the given buffer.\n\n"                          \
    "Takes in a string containing the data to disassemble and the\nmode, "      \
    "either 16, 32, or 64. Also takes the PC offset.\nReturns an Instruction"   \
    "object or \nNone if the instruction can't be disassembled."

PyObject *udis_get_instruction(PyObject *self, PyObject *args)
{
	PyObject *pBuffer, *pMode, *pOffset;
	ud_t my_ud_obj;
	long size, mode;
	ssize_t data_length;
    unsigned long long offset;
    char *data;
    
    
	if(!args || PyObject_Length(args)!=3) {
		PyErr_SetString(PyExc_TypeError,
                        "Invalid number of arguments, 3 expected: (data, mode, pc)");
		return NULL;
	}
	
	pBuffer = PyTuple_GetItem(args, 0);
	if(!check_object(pBuffer)) {
        PyErr_SetString(PyExc_ValueError, "Can't get buffer from arguments");
    }
    
	pMode = PyTuple_GetItem(args, 1);
	if(!check_object(pMode)) {
        PyErr_SetString(PyExc_ValueError, "Can't get mode from arguments");
    }
    mode = PyLong_AsLong(pMode);
    switch (mode) {
        case 16:
        case 32:
        case 64:
            break;
        default:
            PyErr_SetString(PyExc_ValueError, "Invalid mode, must be 16, 32, or 64");
            return NULL;
    }
    pOffset = PyTuple_GetItem(args, 2);
    if(!check_object(pOffset)) {
        PyErr_SetString(PyExc_ValueError, "Can't get offset from arguments");
    }
    offset = PyInt_AsUnsignedLongLongMask(pOffset);
    
    PyString_AsStringAndSize(pBuffer, &data, &data_length);
	
    ud_init(&my_ud_obj);
    ud_set_input_buffer(&my_ud_obj, (unsigned char*)data, data_length);
    ud_set_mode(&my_ud_obj,  mode);
    ud_set_pc(&my_ud_obj, offset);
    ud_set_syntax(&my_ud_obj, NULL);
    size = ud_disassemble(&my_ud_obj);
    
    if(!size) {
        Py_INCREF(Py_None);
        return Py_None;
    }
    
    return create_instruction_object(&my_ud_obj);
}


/*
 Map all the exported methods.
 */
static PyMethodDef udis86Methods[] = {
	{"get_instruction", udis_get_instruction, METH_VARARGS,
        GET_INSTRUCTION_DOCSTRING},
	{NULL, NULL, 0, NULL}
};


/*
 Init the module, set constants.
 */
PyMODINIT_FUNC initpyudis86(void)
{
    int i;
    PyObject *pModule;
    
	pModule = Py_InitModule("pyudis86", udis86Methods);
    
    assign_attribute(pModule, "MODE_16", PyLong_FromLong(1));
    assign_attribute(pModule, "MODE_32", PyLong_FromLong(0));
    assign_attribute(pModule, "MODE_64", PyLong_FromLong(2));
    
    for(i=0; instruction_types[i]; i++)
        assign_attribute(pModule, instruction_types[i], PyLong_FromLong(i));
    
    for(i=0; operand_types[i]; i++)
        assign_attribute(pModule, operand_types[i], PyLong_FromLong(i));
        
}


int main(int agrc, char *argv[])
{
	Py_SetProgramName(argv[0]);
	
	Py_Initialize();
	
	initpyudis86();
    
	return 0;
}

